#include "usbd_cdc_implement.h"


USBD_CDC_HandleTypeDef CDCHandler;

/**
  * @brief  CDC_Implement_Setup
  *         处理 CDC 特定类请求
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
void CDC_Implement_Endpoint_Open(USBD_HandleTypeDef *pdev)
{
    if (pdev->dev_speed == USBD_SPEED_HIGH)
    {
        /* Open EP IN */
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
                    CDC_DATA_HS_IN_PACKET_SIZE);

        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;

        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,
                    CDC_DATA_HS_OUT_PACKET_SIZE);

        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;

    }
    else
    {
        /* Open EP IN */
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
                    CDC_DATA_FS_IN_PACKET_SIZE);

        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;

        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,
                    CDC_DATA_FS_OUT_PACKET_SIZE);

        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
    }
    /* Open Command IN EP */
    USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
    pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;
}


void CDC_Implement_Endpoint_Close(USBD_HandleTypeDef *pdev)
{
    /* Close EP IN */
    USBD_LL_CloseEP(pdev, CDC_IN_EP);
    pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 0U;

    /* Close EP OUT */
    USBD_LL_CloseEP(pdev, CDC_OUT_EP);
    pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 0U;

    /* Close Command IN EP */
    USBD_LL_CloseEP(pdev, CDC_CMD_EP);
    pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 0U;
}


/**
  * @brief  CDC_Implement_Setup_Class_Interface
  *         处理 CDC 接口 的 特定类请求
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
void CDC_Implement_Setup_Class_Interface(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
    USBD_CDC_HandleTypeDef *hcdc = USBD_GETHANDLE_CDC(pdev);
    
    if (req->wLength)
    {
        if (req->bmRequest & 0x80U)
        {
            USBD_GETITF_CDC(pdev)->Control(req->bRequest, (uint8_t *)(void *)hcdc->data,
                            req->wLength);

            USBD_CtlSendData(pdev, (uint8_t *)(void *)hcdc->data, req->wLength);
        }
        else
        {
            hcdc->CmdOpCode = req->bRequest;
            hcdc->CmdLength = (uint8_t)req->wLength;

            USBD_CtlPrepareRx(pdev, (uint8_t *)(void *)hcdc->data, req->wLength);
        }
    }
    else
    {
        USBD_GETITF_CDC(pdev)->Control(req->bRequest, (uint8_t *)(void *)req, 0U);
    }
}


/**
  * @brief  CDC_Implement_DataIn 
  *         CDC 数据发送
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
uint8_t  CDC_Implement_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
    USBD_CDC_HandleTypeDef *hcdc = USBD_GETHANDLE_CDC(pdev);
    PCD_HandleTypeDef *hpcd = pdev->pData;

    if (USBD_GETHANDLE_CDC(pdev) != NULL)
    {
        if ((pdev->ep_in[epnum].total_length > 0U) && ((pdev->ep_in[epnum].total_length % hpcd->IN_ep[epnum].maxpacket) == 0U))
        {
        /* Update the packet total length */
        pdev->ep_in[epnum].total_length = 0U;
  
        /* Send ZLP */
        USBD_LL_Transmit(pdev, epnum, NULL, 0U);
        }
        else
        {
            hcdc->TxState = 0U;
        }
        return USBD_OK;
    }
    else
    {
      return USBD_FAIL;
    }
}

/**
  * @brief  USBD_CDC_DataOut
  *         CDC数据接收
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t  CDC_Implement_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
    USBD_CDC_HandleTypeDef *hcdc = USBD_GETHANDLE_CDC(pdev);
 
    /* Get the received data length */
    hcdc->RxLength = USBD_LL_GetRxDataSize(pdev, epnum);
    
    /* USB data will be immediately processed, this allow next USB traffic being
    NAKed till the end of the application Xfer */
    if (USBD_GETHANDLE_CDC(pdev) != NULL)
    {
        ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength);

        return USBD_OK;
    }
    else
    {
        return USBD_FAIL;
    }
}

/**
  * @brief  CDC_Implement_EP0_RxReady
  *         Handle EP0 Rx Ready event
  * @param  pdev: device instance
  * @retval status
  */
uint8_t CDC_Implement_EP0_RxReady(USBD_HandleTypeDef *pdev)
{
    USBD_CDC_HandleTypeDef   *hcdc = USBD_GETHANDLE_CDC(pdev);

    if ((USBD_GETITF_CDC(pdev) != NULL) && (hcdc->CmdOpCode != 0xFFU))
    {
        USBD_GETITF_CDC(pdev)->Control(hcdc->CmdOpCode,
                                    (uint8_t *)(void *)hcdc->data,
                                    (uint16_t)hcdc->CmdLength);
        hcdc->CmdOpCode = 0xFFU;

    }
    return USBD_OK;
}